package controllers

import (
	"database/sql"
	"gateway-api/tools"
	"strconv"

	"github.com/dtm-labs/client/dtmcli"
	"github.com/gin-gonic/gin"
	"github.com/go-resty/resty/v2"
	"github.com/lithammer/shortuuid/v3"
)

/*
 +----------------------------------------------------------------------
 + Title        : XaController
 + Author       : huwl
 + Version      : 2.0.0
 + Initial-Time : 2020-11-23
 + Last-time    : 这个文件最后修改的时间 + 修改人的名称
 + Desc         : DTM XA 分布式事务控制层 需要创建数据库/表: dtm_barrier.barrier
 + 		        : 总结:
 +					XA与TCC一样是同步执行的执行一个子事务就等待返回一个结果
 +					XA事务的特点是：
 +						简单易理解
 +						开发较容易，回滚之类的操作，由底层数据库自动完成
 +						对资源进行了长时间的锁定，并发度低，不适合高并发的业务
 +
 + SUCCESS      : 表示成功
 + FAILURE      : 表示失败，这个失败是指确定的失败结果，不需要重试。
                  例如子事务因转账余额不足而失败，dtm服务因为事务已完成不允许再次提交等
 + ONGOING      : 表示未完成，还在正常进行中，此时dtm服务器需要采用固定间隔重试，而不是指数退避算法重试
				  对tcc不管用,在tcc模式下和FAILURE状态一样

 + other status : 表示临时错误，采用指数退避算法重试，避免出现故障或者bug，导致大量重试，导致负载过高
 + SUCCESS http : 状态码 200； grpc： err == nil
 + FAILURE http : 状态码 409 (http.StatusConflict)；grpc Code == Aborted tcc 失败、未完成的状态必须使用409、425
 + ONGOING http : 状态码 425 (http.StatusTooEarly)；grpc Code == FailedPrecondition tcc 失败、未完成的状态必须使用409、425
 +----------------------------------------------------------------------
*/
type XaController struct {
	BaseController
}

var BusiConf = dtmcli.DBConf{
	Driver:   "mysql",
	Host:     "127.0.0.1",
	Port:     3306,
	User:     "root",
	Password: "19891201",
}

/**
 * xa grpc 方式
 *
 * @return Json
 * @author huwl
 */
func (control XaController) XaGrpc(ctx *gin.Context) {
	// transInUid, _ := strconv.Atoi(ctx.DefaultQuery("trans_in_uid", "0"))
	// transOutUid, _ := strconv.Atoi(ctx.DefaultQuery("trans_out_uid", "0"))
	// amount, _ := strconv.Atoi(ctx.DefaultQuery("amount", "30"))
	// transInResult := ctx.DefaultQuery("trans_in_result", "")
	// transOutResult := ctx.DefaultQuery("trans_out_result", "")

	// // 获取微服务地址
	// target, err := tools.GetServiceAddress(tools.ServiceName)
	// if err != nil {
	// 	control.BaseError(ctx, control.WithMessage(err.Error()))
	// 	return
	// }

	gid := shortuuid.New()
	// err = dtmgrpc.XaGlobalTransaction(tools.DtmGrpcServer, gid, func(xa *dtmgrpc.XaGrpc) error {
	// 	r := &emptypb.Empty{}
	// 	err := xa.CallBranch(&pbDemo.ReqGrpc{
	// 		Uid:            int64(transOutUid),
	// 		Amount:         int64(amount),
	// 		TransOutResult: transOutResult,
	// 		TransInResult:  transInResult,
	// 	}, target+"/demo.Busi/TransOutXa", r)
	// 	if err != nil {
	// 		return err
	// 	}
	// 	err = xa.CallBranch(&pbDemo.ReqGrpc{
	// 		Uid:            int64(transInUid),
	// 		Amount:         int64(amount),
	// 		TransOutResult: transOutResult,
	// 		TransInResult:  transInResult,
	// 	}, target+"/demo.Busi/TransInXa", r)
	// 	return err
	// })
	// if err != nil {
	// 	control.BaseError(ctx, control.WithMessage(err.Error()))
	// 	return
	// }

	control.BaseSuccess(ctx, control.WithData(gin.H{
		"gid": gid,
	}))
}

/**
 * xa http 方式
 *
 * @return Json
 * @author huwl
 */
func (control XaController) XaHttp(ctx *gin.Context) {
	transInUid, _ := strconv.Atoi(ctx.DefaultQuery("trans_in_uid", "0"))
	transOutUid, _ := strconv.Atoi(ctx.DefaultQuery("trans_out_uid", "0"))
	amount, _ := strconv.Atoi(ctx.DefaultQuery("amount", "30"))
	transInResult := ctx.DefaultQuery("trans_in_result", "")
	transOutResult := ctx.DefaultQuery("trans_out_result", "")

	gid := shortuuid.New()
	err := dtmcli.XaGlobalTransaction(tools.DtmHttpServer, gid, func(xa *dtmcli.Xa) (*resty.Response, error) {
		// 转出子事务
		resp, err := xa.CallBranch(&ReqHTTP{
			Uid:            transOutUid,
			Amount:         amount,
			TransOutResult: transOutResult,
			TransInResult:  transInResult,
		}, tools.HttpServiceUrl+"/xa/transOutXaGorm")
		if err != nil {
			return resp, err
		}

		// 转入子事务
		return xa.CallBranch(&ReqHTTP{
			Uid:            transInUid,
			Amount:         amount,
			TransOutResult: transOutResult,
			TransInResult:  transInResult,
		}, tools.HttpServiceUrl+"/xa/transInXa")
	})
	if err != nil {
		control.BaseError(ctx, control.WithMessage(err.Error()))
		return
	}

	control.BaseSuccess(ctx, control.WithData(gin.H{
		"gid": gid,
	}))
}

/**
 * xa 转出
 *
 * @return Json
 * @author huwl
 */
func (control XaController) TransOutXaGorm(ctx *gin.Context) {
	result := ctx.DefaultPostForm("trans_out_result", "")
	amount, _ := strconv.ParseInt(ctx.DefaultPostForm("amount", "0"), 10, 64)
	uid, _ := strconv.Atoi(ctx.DefaultPostForm("uid", "0"))

	err := dtmcli.XaLocalTransaction(ctx.Request.URL.Query(), BusiConf, func(db *sql.DB, xa *dtmcli.Xa) error {
		if uid <= 0 {
			return dtmcli.ErrFailure
		}

		if result == "FAILURE" {
			return dtmcli.ErrFailure
		}

		return tools.DatabaseManager.Exec("update trident_mall.mall_user set balance=balance-? where uid=?", amount, uid).Error
	})

	if err != nil {
		control.BaseError(ctx, control.WithMessage(err.Error()))
		return
	}

	control.BaseSuccess(ctx, control.WithData(gin.H{}))
}

/**
 * xa 转入
 *
 * @return Json
 * @author huwl
 */
func (control XaController) TransInXa(ctx *gin.Context) {
	result := ctx.DefaultPostForm("trans_in_result", "")
	amount, _ := strconv.ParseInt(ctx.DefaultPostForm("amount", "0"), 10, 64)
	uid, _ := strconv.Atoi(ctx.DefaultPostForm("uid", "0"))
	err := dtmcli.XaLocalTransaction(ctx.Request.URL.Query(), BusiConf, func(db *sql.DB, xa *dtmcli.Xa) error {
		if uid <= 0 {
			return dtmcli.ErrFailure
		}

		if result == "FAILURE" {
			return dtmcli.ErrFailure
		}

		return tools.DatabaseManager.Exec("update trident_mall.mall_user set balance=balance+? where uid=?", amount, uid).Error
	})

	if err != nil {
		control.BaseError(ctx, control.WithMessage(err.Error()))
		return
	}

	control.BaseSuccess(ctx, control.WithData(gin.H{}))
}
